package org.textreport.core;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext;

import org.textreport.lang.Expression;

public class ExecutionContext {

	private SimpleScriptContext ctx;
	private ScriptEngine engine;
	private SQLTypeFactory sqlTypeFactory;
	private OutBuffer out;
	private Collection<Runnable> cleanTasks = new ArrayList<Runnable>();
	
	private ExecutionContext() {
	}

	/**
	 * @param args
	 */
	public static ExecutionContext build() throws Exception {
		ExecutionContext r = new ExecutionContext();
		ScriptEngineManager manager = new ScriptEngineManager();
		r.engine = manager.getEngineByName("JavaScript");
		r.ctx = new SimpleScriptContext();
		r.out = new OutBuffer();

		r.prepareEvalContext();

		return r;
	}
	
	public void addCleanupTask(Runnable r){
		cleanTasks.add(r);
	}
	
	public Collection<Runnable> getCleanupTasks(){
		return cleanTasks;
	}

	public OutBuffer getOut() {
		return out;
	}

	public void error(String type, Expression exp, String msg, Throwable er) {
		Throwable cause = er;
		String emsg = null;
		
		if(er != null){
			emsg = er.getMessage();
			while (cause != null && (cause = cause.getCause()) != null) {
				if (cause.getMessage() != null) {
					emsg = cause.getMessage();
				}
				
				cause = cause.getCause();
			}
		}

		throw new IllegalStateException("linha[" + exp.getSourceLine() + "] " + type + ": " + (msg != null ? msg : "") + " " + (emsg != null ? emsg : ""), er);
	}

	private void prepareEvalContext() {
		try {
			ctx.setAttribute("_ectx", this, ScriptContext.ENGINE_SCOPE);
			ctx.setAttribute("_ctxFunctions", new Functions(), ScriptContext.ENGINE_SCOPE);
			ctx.setAttribute("outbuf", out, ScriptContext.ENGINE_SCOPE);

			engine.eval(new InputStreamReader(this.getClass().getResourceAsStream("basics.js")), ctx);

		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	public void putVariable(String name, Object value) {
		ctx.setAttribute(name, value, ScriptContext.ENGINE_SCOPE);
	}

	public void unset(String name) {
		ctx.removeAttribute(name, ScriptContext.ENGINE_SCOPE);
	}

	public Object getVariable(String name) {
		return ctx.getAttribute(name, ScriptContext.ENGINE_SCOPE);
	}
	
	public boolean variableIsDefined(String name){
		return ctx.getAttributesScope(name) == ScriptContext.ENGINE_SCOPE;
	}

	public Object evalJavaScript(String src) throws Exception {
		return engine.eval(src, ctx);
	}

	public SQLTypeFactory getSqlTypeFactory() {
		return sqlTypeFactory;
	}

	public void setSqlTypeFactory(SQLTypeFactory sqlTypeFactory) {
		this.sqlTypeFactory = sqlTypeFactory;
	}

}
